﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using TtfLibrary.CommonStructures;

namespace TtfLibrary
{
    public class TtfFileWriter
    {
        public TtfFile TtfFile;
        public StructureWriter Writer;

        public TtfFileWriter(TtfFile file, Stream outputStream)
        {
            TtfFile = file;
            Writer = new StructureWriter() { Stream = outputStream };
        }

        public void Write()
        {
            var nonExistsDirectories = TtfFile.Tables.Keys.Except(TtfFile.TableDictionaries.Keys);
            foreach (var tableName in nonExistsDirectories)
                TtfFile.TableDictionaries[tableName] = new TtfTableDictionary() { TagAsString = tableName };


            TtfFile.OffsetTable.ScalerType = new TtfFixed() { Major = 1, Minor = 0 };
            TtfFile.OffsetTable.NumTables = (UInt16)TtfFile.Tables.Count;
            var pow = (int)Math.Log(TtfFile.OffsetTable.NumTables, 2);
            TtfFile.OffsetTable.SearchRange = (UInt16)(Math.Pow(2, pow) * 16);
            TtfFile.OffsetTable.EntrySelector = (UInt16)pow;
            TtfFile.OffsetTable.RangeShift = (UInt16)(TtfFile.OffsetTable.NumTables * 16 - TtfFile.OffsetTable.SearchRange);

            Writer.WriteObject(TtfFile.OffsetTable);

            var tdOffset = Writer.Stream.Position;
            Writer.Stream.Position += TtfTableDictionary.ContentLength * TtfFile.TableDictionaries.Count;

            TtfFile.Head.CheckSumAdjustment = 0;

            var tableNamesInOrder = TtfFile.TableDictionaries.Keys.OrderBy(tableName => TtfFile.TableOrders.IndexOf(tableName)).ToArray();
            //var tableNamesInOrder = TtfFile.TableDictionaries.Keys.OrderBy(k => k);
            foreach (var tableName in tableNamesInOrder)
            {
                Writer.StartRegion();
                WriteTable(tableName);
                var region = Writer.EndRegion();
                TtfFile.TableDictionaries[tableName].Offset = (uint)region.StartPosition;
                TtfFile.TableDictionaries[tableName].Length = (uint)region.Length;
                TtfFile.TableDictionaries[tableName].CheckSum = region.CalculateChecksum();
                region.Padding();
            }

            Writer.Stream.Position = tdOffset;
            /*var order = TtfFile.TableDictionaries.OrderBy(t =>
            {
                var i = TtfFile.TableDictionaryNamesInOrder.IndexOf(t.Key);
                return i == -1 ? int.MaxValue : i;
            });

            foreach (var tdKvp in order)
                Writer.WriteObject(tdKvp.Value);*/

            //var order = TtfFile.TableDictionaries.Keys.OrderBy(k => k == "head" ? "\0" : k, StringComparer.Ordinal);
            var order = TtfFile.TableDictionaries.Keys.OrderBy(k => k, StringComparer.Ordinal);
            foreach (var td in order.Select(tableName => TtfFile.TableDictionaries[tableName]))
                Writer.WriteObject(td);


            Writer.Stream.Position = 0;
            var buf = new byte[Writer.Stream.Length];
            Writer.Stream.Read(buf, 0, buf.Length);
            var sum = CalculateChecksum(buf);

            TtfFile.Head.CheckSumAdjustment = 0xB1B0AFBA - sum;
            Writer.Stream.Position = TtfFile.TableDictionaries["head"].Offset;
            Writer.WriteObject(TtfFile.Head);
        }

        public bool OriginalWrite = false;

        public void WriteTable(string tableName)
        {
            if (OriginalWrite && !new[] { "head" }.Contains(tableName))
                Writer.WriteBytes(TtfFile.TableContents[tableName]);
            else
                Writer.WriteObject(TtfFile.Tables[tableName]);
        }

        public static UInt32 CalculateChecksum(byte[] tableContent)
        {
            UInt32 sum = 0;
            for (int i = 0; i < tableContent.Length / 4 + 1; i++)
            {
                var b1 = i * 4 + 0 >= tableContent.Length ? 0 : tableContent[i * 4 + 0];
                var b2 = i * 4 + 1 >= tableContent.Length ? 0 : tableContent[i * 4 + 1];
                var b3 = i * 4 + 2 >= tableContent.Length ? 0 : tableContent[i * 4 + 2];
                var b4 = i * 4 + 3 >= tableContent.Length ? 0 : tableContent[i * 4 + 3];
                sum += (UInt32)(b1 << 24 | b2 << 16 | b3 << 8 | b4);
            }

            return sum;
        }
    }

    public interface IAfterWrite
    {
        void AfterWrite(StructureWriter writer);
    }
}
